#include<iostream>
#include<mutex>
using namespace std;

namespace Mango
{
	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr) :_ptr(ptr)
		{
			if (ptr != nullptr)
			{
				_pCount = new int(1);
				_mtx = new mutex();
			}
		}
		void AddRefCount()
		{
			_mtx->lock();
			(*_pCount)++;
			_mtx->unlock();
		}
		void ReleaseRefCount()
		{
			unique_lock<mutex> ul(*_mtx);
			bool flag = false;
			if (_ptr && --(*_pCount) == 0)
			{
				flag = true;
				delete _pCount;
				delete _ptr;
				_pCount = _ptr = nullptr;
			}
			if (flag)
			{
				ul.unlock();
				delete _mtx;
				_mtx = nullptr;
			}
		}
		shared_ptr(shared_ptr<T>& sp) :_ptr(nullptr)
		{
			_ptr = sp._ptr;
			_mtx = sp._mtx;
			_pCount = sp._pCount;
			AddRefCount();
		}
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				ReleaseRefCount();
				_ptr = sp._ptr; 
				_mtx = sp._mtx;
				_pCount = sp._pCount;
				AddRefCount();
			}
			return *this;
		}
		~shared_ptr()
		{
			ReleaseRefCount();
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}
		int use_count()
		{
			return *_pCount;
		}
private:
	T* _ptr;
	mutex* _mtx;
	int* _pCount;
};
}
int main()
{
	Mango::shared_ptr<int> sp(new int(1));
	Mango::shared_ptr<int> sp2 = sp;
	Mango::shared_ptr<int> sp3 = new int(3);
	cout << *sp << endl;
	*sp2 = 20;
	cout << *sp2 << endl;
	cout << sp.use_count() << endl;

	cout << "--------------------" << endl;
	sp2 = sp3;
	cout << sp.use_count() << endl;
	cout << sp3.use_count() << endl;
	return 0;
}